
#include <BPainter>
#include <BRender>
#include <BKeyEvent>
#include <BMouseEvent>
#include <BRoundHandler>

#include <BCamera>
#include <BViewer>
#include <BShape>
#include <BMaterial>
#include <BOpenGL>
#include <BSystem>
#include <BScene>

#include "StringUtils.h"
#include "MainHandler.h"
#include "DragHandler.h"

DragHandler::DragHandler()
{
	BRoundHandler* handler = new BRoundHandler();
	this->addChild(handler);

	_leftPressed = false;
	_spaceShown = true;

	_pointRestraint = new BPointRestraint();
	_pointRestraint->setImpulseLimit(30);
	_pointRestraint->setBodyB(new BBody());
}
DragHandler::~DragHandler()
{

}

BPointRestraint* DragHandler::pointRestraint()
{
	return _pointRestraint;
}

void DragHandler::freshDragRing(BReal axisSize)
{
	BReal unit = 2 * PI / 24;
	_dragRingPoints.resize(50);
	for (int i = 0; i < 25; i++)
	{
		BReal r = i * unit;
		BVector v0(cos(r) * axisSize, sin(r) * axisSize, 0);
		BVector v1(cos(r) * axisSize / 2, sin(r) * axisSize / 2, 0);
		_dragRingPoints[i * 2 + 0] = v0;
		_dragRingPoints[i * 2 + 1] = v1;
	}
}

void DragHandler::paint(BPainter& painter)
{
	if (_pickedHit)
	{
		const BVector& velocityError = _pointRestraint->query("velocity-error");
		BString text;
		text << velocityError.x() << '\n';
		text << velocityError.y() << '\n';
		text << velocityError.z();
		BPoint point = this->viewer()->point(_pickedHit->absolutePoint());
		BSize textSize = painter.realFont()->textSize(text);
		BRect textRect(point + 20, textSize + 10);
		painter.setColor(230, 230, 230);
		painter.fillRoundRect(textRect);
		painter.setColor(Color_Text);
		painter.drawText(textRect, text, Align_RightCenter);
	}
}
void DragHandler::render(BRender& render)
{
	render.setLighting(false);
	if (_pickedHit)
	{
		render.setPointSize(6);
		render.setColor(255, 0, 0);
		render.drawVertex(_pickedHit->absolutePoint());
		if (_pointRestraint)
		{
			render.setLineWidth(1);
			render.setColor(0, 0, 255);
			//render.drawVertex(_pointRestraint->pivotB());
			BVector pivotB = _pointRestraint->pivotB();
			render.drawVertex(pivotB.x(), pivotB.y(), pivotB.z());
			render.setColor(0, 255, 0);
			render.drawLine(_pickedHit->absolutePoint(), BVector(pivotB.x(), pivotB.y(), pivotB.z()));
		}
	}
}

void DragHandler::attached(BViewer* viewer)
{
	viewer->setCursor(Cursor_Hand);
	this->scene()->insert(_pointRestraint);
}
void DragHandler::detached(BViewer* viewer)
{
	viewer->setCursor(Cursor_None);
	this->scene()->remove(_pointRestraint);
}

void DragHandler::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		const BPoint& pos = mouseEvent.pos();
		_pickedHit = this->viewer()->pick(pos);
		if (_pickedHit)
		{
			BVector pickedPoint = _pickedHit->absolutePoint();
			_pickedBody = dynamic_cast<BBody*>(_pickedHit->node());
			if (_pickedBody)
			{
				if (_pickedBody->mass() > REAL_EPSILON)
				{
					const BString& name = _pickedBody->name();
					_pickedBody->setState(BBody::State_Active);
					BVector localPivot = _pickedBody->matrix().inverse() * pickedPoint;
					_pointRestraint->setBodyA(_pickedBody);
					_pointRestraint->setPivotA(localPivot);
					_pointRestraint->setPivotB(pickedPoint);
					_pointRestraint->setEnabled(true);
				}
				BLine line = this->viewer()->line(pos);
				_pickingDist = (pickedPoint - line.begin()).length();
			}
			_leftPressed = true;
		}
	}
	mouseEvent.accept();
}
void DragHandler::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		_pointRestraint->setEnabled(false);
		_pickedHit = 0;
		_leftPressed = false;
	}
	mouseEvent.accept();
}
void DragHandler::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	BLine line = this->viewer()->line(pos);
	if (_leftPressed && _pickedBody && _pointRestraint)
	{
		const BVector& begin = line.begin();
		const BVector& end = line.end();
		BVector rayFrom(begin.x(), begin.y(), begin.z());
		BVector rayTo(end.x(), end.y(), end.z());
		BVector dir = rayTo - rayFrom;
		dir.normalize();
		dir *= _pickingDist;
		BVector newPivotB = rayFrom + dir;
		_pointRestraint->setPivotB(newPivotB);
	}
	mouseEvent.accept();
}

